WeakReference = {
    __mode = 'v'
}

function WeakReference.new(value)
    local t = {
        Ref = value
    }
    setmetatable(t, WeakReference)
    return t
end

CompoundBindableProperty = {}

local get = {}
local set = {}

CompoundBindableProperty.__cname = 'CompoundBindableProperty'
CompoundBindableProperty.__index = function(t,k)
    local var = rawget(CompoundBindableProperty, k)

    if var == nil then
        var = rawget(get, k)

        if var ~= nil then
            return var(t)
        end
    end

    return var
end

CompoundBindableProperty.__tostring = function(self)
    local value = rawget(self, '_value')
    if value then
        return tostring(value)
    else
        return 'nil'
    end
end

CompoundBindableProperty.__newindex = function(t, k, v)
    local var = rawget(CompoundBindableProperty, k)

    if var == nil then
        var = rawget(set, k)

        if var ~= nil then
            return var(t, v)
        end
    end
end

function CompoundBindableProperty.new(bindableProperties, func, delay_execute)
    local t = {
        bindableProperties = bindableProperties,
        oldParams = nil,
        oldValue = nil,
        func = func,
        OnValueChanged = {},
        co = nil,
        switch = true,
        delay_execute = delay_execute,
        onChange = nil
    }
    local weak_self = WeakReference.new(t)

    local setOldParams = function(ref)
        local oldParams = {}
        for _, property in ipairs(bindableProperties) do
            if property.class == CompoundBindableProperty then
                table.insert(oldParams, property.oldValue)
            else
                table.insert(oldParams, property.Value)
            end
        end
        rawset(ref, 'oldParams', oldParams)
    end
    setOldParams(t)

    local onChange = function(oldValue, newValue)
        if weak_self.Ref then
            local ref = weak_self.Ref
            if ref.switch then
                if ref.delay_execute then
                    if ref.co == nil then --这里是为了避免在同一帧内被调用多次导致界面多次刷新，这里记录了一个事件，并且在下一帧统一处理所有的变化
                        rawset(ref, 'co', TIMERM.Step(function()
                            CompoundBindableProperty.OnContainValueChanged(ref)
                            setOldParams(ref)
                            rawset(ref, 'co', nil)
                        end, nil, 1, 1))
                    end
                else
                    CompoundBindableProperty.OnContainValueChanged(ref)
                    setOldParams(ref)
                end
            end
            return false
        else
            return true
        end
    end

    for _, property in ipairs(t.bindableProperties) do
        table.insert(property.OnValueChanged, onChange)
    end

    rawset(t, 'onChange', onChange)

    local prox = newproxy(true)
    getmetatable(prox).__gc = function() t:Destroy() end
    t[prox] = true

    setmetatable(t, CompoundBindableProperty)
    return t
end

function CompoundBindableProperty:Destroy()
    if rawget(self, 'onChange') == nil then
        return
    end

    for _, property in ipairs(self.bindableProperties) do
        table.remove_value(property.OnValueChanged, rawget(self, 'onChange'))
    end

    rawset(self, 'onChange', nil)
end

function CompoundBindableProperty:Set_Value(value)

end

function CompoundBindableProperty:Get_Value()
    self:OnContainValueChanged(true)
    local value = self.oldValue
    return value
end

function CompoundBindableProperty:Switch(on)
    rawset(self, 'switch', on)
end

function CompoundBindableProperty:OnContainValueChanged(onlyCalculateValue)
    onlyCalculateValue = onlyCalculateValue or false
    if self.func then
        local args = {}
        local properties = rawget(self, 'bindableProperties')
        for _, property in pairs(properties) do
            table.insert(args, property.Value)
        end
        local result = self.func(unpack(args))
        if not onlyCalculateValue then
            for _, onValueChanged in pairs(self.OnValueChanged) do
                onValueChanged(self.oldValue, result)
            end
        end
        rawset(self, 'oldValue', result)
    end
end

get.Value = CompoundBindableProperty.Get_Value
set.Value = CompoundBindableProperty.Set_Value